package com.fs.web.yz.utils;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fs.common.pojo.CheckNote;
import com.fs.common.pojo.CheckRecipes;
import com.fs.common.pojo.Recipes;
import com.fs.common.pojo.RecipesTemplate;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

/**
 * 菜谱生成工具类
 */
public class RecipesUtils {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    public static final int Month_First = 1;

    public static final int Month_Last = 28;

    /**
     * 早餐 主食
     */
    private List<Recipes> breakfastzsList = new ArrayList<>(16),
    /**
     * 早餐 辅食
     */
    breakfastfsList = new ArrayList<>(16);
    /**
     * 中餐 主食
     */
    private List<Recipes> lunchzsList = new ArrayList<>(16),
    /**
     * 中餐 主菜
     */
    lunchzcList = new ArrayList<>(16),
    /**
     * 中餐 辅菜
     */
    lunchfcList = new ArrayList<>(16);
    /**
     * 晚餐 主食
     */
    private List<Recipes> supperzsList = new ArrayList<>(16),
    /**
     * 晚餐 主菜
     */
    supperzcList = new ArrayList<>(16),
    /**
     * 晚餐 辅菜
     */
    supperfcList = new ArrayList<>(16);
    /**
     * 加餐
     */
    private List<Recipes> snackList = new ArrayList<>(16);

    private Map<Integer, String> Num2MealFood = new HashMap<>(10);

    private Map<String, Collection<Recipes>> mealfood2Recipes = new HashMap<>(9);

    /**
     * 早餐
     */
    private static final int MEAL_BREAKFAST = 1;
    /**
     * 午餐
     */
    private static final int MEAL_LUNCH = 2;
    /**
     * 晚餐
     */
    private static final int MEAL_SUPPER = 3;
    /**
     * 加餐
     */
    private static final int MEAL_SNACK = 4;

    /**
     * 主食
     */
    private static final int FOOD_ZHUSHI = 11;
    /**
     * 辅食
     */
    private static final int FOOD_FUSHI = 12;
    /**
     * 主菜
     */
    private static final int FOOD_ZHUCAI = 13;
    /**
     * 辅菜
     */
    private static final int FOOD_FUCAI = 14;
    /**
     * 甜品
     */
    private static final int FOOD_TIANPIN = 15;
    /**
     * 饮品
     */
    private static final int FOOD_YINPIN = 16;

    private static final String breakfast = "早餐", lunch = "中餐", supper = "晚餐", snack = "加餐";
    private static final String zhushi = "主食", zhucai = "主菜", fushi = "辅食", fucai = "辅菜";
    private static final String tianpin = "甜品", yinpin = "饮品";

    private static final int[] SNACK_Arr = {FOOD_TIANPIN, FOOD_YINPIN, FOOD_TIANPIN, FOOD_YINPIN, FOOD_YINPIN, FOOD_TIANPIN};

    private static final String[] deliveryArr = {"顺产", "剖腹产"};
    private static final String[] feedArr = {"母乳", "奶粉"};
    {
        Num2MealFood.put(MEAL_BREAKFAST, breakfast);
        Num2MealFood.put(MEAL_LUNCH, lunch);
        Num2MealFood.put(MEAL_SUPPER, supper);
        Num2MealFood.put(MEAL_SNACK, snack);
        Num2MealFood.put(FOOD_ZHUSHI, zhushi);
        Num2MealFood.put(FOOD_FUSHI, fushi);
        Num2MealFood.put(FOOD_ZHUCAI, zhucai);
        Num2MealFood.put(FOOD_FUCAI, fucai);
        Num2MealFood.put(FOOD_TIANPIN, tianpin);
        Num2MealFood.put(FOOD_YINPIN, yinpin);

        mealfood2Recipes.put(getMealFoodCode(MEAL_BREAKFAST, FOOD_ZHUSHI), breakfastzsList);
        mealfood2Recipes.put(getMealFoodCode(MEAL_BREAKFAST, FOOD_FUSHI), breakfastfsList);
        mealfood2Recipes.put(getMealFoodCode(MEAL_LUNCH, FOOD_ZHUSHI), lunchzsList);
        mealfood2Recipes.put(getMealFoodCode(MEAL_LUNCH, FOOD_ZHUCAI), lunchzcList);
        mealfood2Recipes.put(getMealFoodCode(MEAL_LUNCH, FOOD_FUCAI), lunchfcList);
        mealfood2Recipes.put(getMealFoodCode(MEAL_SUPPER, FOOD_ZHUSHI), supperzsList);
        mealfood2Recipes.put(getMealFoodCode(MEAL_SUPPER, FOOD_ZHUCAI), supperzcList);
        mealfood2Recipes.put(getMealFoodCode(MEAL_SUPPER, FOOD_FUCAI), supperfcList);
        mealfood2Recipes.put(getMealFoodCode(MEAL_SNACK, FOOD_TIANPIN), snackList);
    }

    private Random random = new Random();
    private static final String high ="2", normal = "1", low ="0";


    /**
     * 根据检查记录生成某一天的菜谱
     * @param cn 检查记录
     * @param day 指定日期
     * @param deliveryDay 分娩日期
     * @param deliveryType 分娩类型
     * @param feedType 喂养方式
     * @param symptomList 检查症状列表
     * @param avoidFoodArr 忌口食物数组
     * @param constitutionArr 检查体质数组
     * @return 对应检查记录的菜谱
     */
    public List<CheckRecipes> getDayCheckRecipes(CheckNote cn, int day, Date deliveryDay, int deliveryType,
                                                 int feedType, ArrayList<String> symptomList,
                                                 String[] avoidFoodArr, String[] constitutionArr){
        RecipesTemplate rtQuery = new RecipesTemplate();
        //记录当天已选的菜谱，不分餐次菜品
        Map<String, RecipesTemplate> chooseMap = new HashMap<>();
        List<CheckRecipes> chooseRecipes = new ArrayList<>();
        int week = getWeek(day);
        String code = null;
        List<int[]> meals = getMealsSetting();
        Date eatDate = DateUtils.addDays(deliveryDay, day-1);
        //餐次 早餐 午餐 晚餐 加餐
        for(int idx=0, len=meals.size(); idx<len; idx++){
            int[] foods = meals.get(idx);
            int meal = idx+1;
            //菜品类型 主食 辅食 主菜 辅菜 甜品 饮品
            for (int food : foods){
                //根据喂养方式分娩方式筛选当天的菜品，根据优先级倒序排列
                QueryWrapper<RecipesTemplate> qw = new QueryWrapper<>();
                qw.like("mode_type", deliveryArr[deliveryType]);
                qw.like("feed_type", feedArr[feedType]);
                code = getCode(week, day, meal, food);
                if (meal == MEAL_SNACK) {
                    qw.likeRight("template_code", code.substring(0,4));
                } else {
                    qw.eq("template_code", code);
                }
                //查询符合基础条件的菜谱
                List<RecipesTemplate> results = rtQuery.selectList(qw);
                //根据忌口、症状和体质筛选菜谱模板
                List<RecipesTemplate> checkTemplate = recipesAfterCheck(results,
                        symptomList, constitutionArr, avoidFoodArr);
                //将通过优先级筛选的菜谱模板加入当天菜谱
                CheckRecipes cr = null;
                if (meal == MEAL_SNACK) {
                    cr = getCheckSnack(checkTemplate, eatDate, cn, week);
                    //
                }else{
                    cr = getCheckRecipes(checkTemplate, eatDate, cn, chooseMap, week);
                }
                if (cr != null && cr.getId() != null) {
//                    logger.info(cr.getEatDate()+"-"+cr.getName()+":"+cr.getMeal()+":"+cr.getFoodType());
                    chooseRecipes.add(cr);
                }
            }
        }
        rtQuery = null;
        return chooseRecipes;
    }

    /**
     * 根据喂养方式分娩方式筛选后的模板菜谱，再根据忌口、症状和体质二次筛选
     * @param templates
     * @param symptomList
     * @param constitutionArr
     * @param avoidFoodArr
     * @return
     */
    private List<RecipesTemplate> recipesAfterCheck(List<RecipesTemplate> templates,
                                      List<String> symptomList, String[] constitutionArr,
                                      String[] avoidFoodArr){
        List<RecipesTemplate> recipes = new ArrayList<>();
        for (RecipesTemplate rt : templates){
            //不适宜症状 notSuitableSymptom
            String nss = rt.getNotSuitable();
            //适宜症状 suitableSymptom
            String ss = rt.getSuitableSymptom();
            //适宜体质 suitableConstitution
            String sc = rt.getSuitable();
            //全部体质
            String allSC = "全", notJoinSS = "不参与", notNSS = "无";
            boolean afFlag = false, nssFlag = false, ssFlag = false, scFlag = false;
            //忌口
            if (avoidFoodArr != null && avoidFoodArr.length > 0) {
                for (String af : avoidFoodArr) {
                    if (StringUtils.isNotBlank(af) && rt.getMainFood().contains(af)) {
                        afFlag = true;
                        break;
                    }
                }
                if (afFlag) {
                    continue;
                }
            }
            //不适宜症状排除
            if (!notNSS.equals(nss)){
                for (String symptom : symptomList){
                    if (StringUtils.isNotBlank(symptom) && nss.contains(symptom)){
                        //出现不适宜的症状
                        nssFlag = true;
                        break ;
                    }
                }
                if (nssFlag){
                    //查看下一个菜谱模板
                    continue ;
                }
            }
            //适宜症状和体质取并集
            //症状优先
            if (!notJoinSS.equals(ss)){
                for (String symptom : symptomList){
                    if (StringUtils.isNotBlank(symptom) && ss.contains(symptom)){
                        ssFlag = true;
                        break ;
                    }
                }
                if (ssFlag){
                    //该菜谱保留
                    recipes.add(rt);
                    //查看下一个菜谱模板
                    continue ;
                }
            }
            if (!allSC.equals(sc)){
                for (String c : constitutionArr){
                    if (StringUtils.isNotBlank(c) && sc.contains(c)){
                        scFlag = true;
                        break;
                    }
                }
            }else{
                scFlag = true;
            }
            if (scFlag) {
                recipes.add(rt);
            }
        }
        return recipes;
    }

    private void sortRecipesTemplates(List<RecipesTemplate> checkTemplate, int week){
        for (RecipesTemplate rt : checkTemplate){
            //2:high  0:low
            if (!"20".contains(rt.getType())){
                String type = rt.getType();
                if (type.contains(".")){
                    String weeks = type.substring(2);
                    if (weeks.contains(week+"")){
                        rt.setType("1.5");
                    }else{
                        rt.setType("1");
                    }
                }
            }
        }
        RecipesTemplateComparator rtc = new RecipesTemplateComparator();
        checkTemplate.sort(rtc);
    }

    /**
     * 筛选菜谱，将优先级高指定时间必须吃的菜谱筛选出来
     * @param recipes 指定时间可以吃的菜谱
     * @param eatDate 吃的日期
     * @param cn 检查记录
     * @param chooseMap 当天已选菜品
     * @param week 周
     * @return 生成对应检查的菜谱
     */
    private CheckRecipes getCheckRecipes(List<RecipesTemplate> recipes, Date eatDate,
                        CheckNote cn, Map<String, RecipesTemplate> chooseMap, int week){
        CheckRecipes rtn = null;
        RecipesTemplate chooseTemplate = null;
        List<RecipesTemplate> bak = new ArrayList<>();
        //对菜谱进行倒序排列，优先级高的优先筛选
        sortRecipesTemplates(recipes, week);
        boolean isChoose = false;
        for (RecipesTemplate rt : recipes){
            //当天已选菜谱存入备选，未选取菜谱直接使用，结束循环
            isChoose = chooseLevel(rt, chooseMap, bak);

            if (isChoose){
                chooseTemplate = rt;
                break;
            }
        }
        int bakLen = bak.size();
        //从备选列表中随机选取加入
        if (chooseTemplate == null && bakLen > 0){
            //随机选取一个菜谱
            int num = random.nextInt(bakLen);
            chooseTemplate = bak.get(num);
        }
        if (chooseTemplate != null) {
            rtn = template2CheckRecipes(chooseTemplate, eatDate, cn);
        }
        return rtn;
    }

    /**
     * 加餐筛选, 首先在优先级高的集合中随机，再普通级随机，最后低级随机
     * @param recipes
     * @param eatDate
     * @param cn
     * @param week
     * @return
     */
    private CheckRecipes getCheckSnack(List<RecipesTemplate> recipes, Date eatDate,
                                       CheckNote cn, int week){
        CheckRecipes rtn = null;
        RecipesTemplate chooseTemplate = null;
        List<RecipesTemplate> highList = new ArrayList<>();
        List<RecipesTemplate> secondList = new ArrayList<>();
        List<RecipesTemplate> normalList = new ArrayList<>();
        List<RecipesTemplate> lowList = new ArrayList<>();
        //对菜谱进行倒序排列，优先级高的优先筛选
        sortRecipesTemplates(recipes, week);
        for (RecipesTemplate rt : recipes){
            if (high.equals(rt.getType())){
                highList.add(rt);
            }else if (low.equals(rt.getType())){
                lowList.add(rt);
            }else if (normal.equals(rt.getType())){
                normalList.add(rt);
            }else{
                secondList.add(rt);
            }
        }
        if (CollectionUtils.isNotEmpty(highList)){
            chooseTemplate = randomList(highList);
        }else if (CollectionUtils.isNotEmpty(secondList)){
            chooseTemplate = randomList(secondList);
        }else if (CollectionUtils.isNotEmpty(normalList)){
            chooseTemplate = randomList(normalList);
        }else if (CollectionUtils.isNotEmpty(lowList)){
            chooseTemplate = randomList(lowList);
        }
        if (chooseTemplate != null) {
            rtn = template2CheckRecipes(chooseTemplate, eatDate, cn);
        }
        return rtn;
    }

    /**
     * 随机从列表中选一个元素
     * @param list 选取元素的集合
     * @return
     */
    private RecipesTemplate randomList(List<RecipesTemplate> list){
        int idx = random.nextInt(list.size());
        return list.get(idx);
    }

    private CheckRecipes template2CheckRecipes(RecipesTemplate rt, Date eatDate, CheckNote cn){
        CheckRecipes rtn = new CheckRecipes();
        rtn.setId(UUID.randomUUID().toString().replace("-",""));
        rtn.setCheckId(cn.getCheckId());
        rtn.setCreaterId(cn.getCreaterId());
        rtn.setEatDate(eatDate);
        rtn.analysisRecipesTemplate(rt);
        return rtn;
    }

    /**
     * 菜谱模板选取，当天未选择过的菜谱直接选取并记录，已选的放入备选列表
     * @param rt
     * @param chooseMap
     * @param bak
     * @return 是否已加入当天菜谱
     */
    private boolean chooseLevel(RecipesTemplate rt, Map<String, RecipesTemplate> chooseMap,
                                List<RecipesTemplate> bak){
        boolean rtn = false;
        switch (rt.getType()){
            case "2":
            case "1.5":
            case "1":
            case "0":
                if (!chooseMap.containsKey(rt.getRecipesId())) {
                    chooseMap.put(rt.getRecipesId(), rt);
                    rtn = true;
                } else {
                    bak.add(rt);
                }
                break;
        }
        return rtn;
    }

    /**
     * 菜谱通过餐次和餐品类别分类
     * @param r 菜谱原始数据
     */
    public void assignRecipes(Recipes r){
        r = changeWeekDay(r);
        //将周和日处理成正则表达式
        String week = r.getWeek();
        String day = r.getDay();
        r.setWeek(changeWeek(week));
        r.setDay(changeDay(day));
        r.setModeType(changeModeType(r.getModeType()));
        r.setFeedType(changeFeedType(r.getFeedType()));

        //分类餐次与食物类别
        String meal = r.getMeal();
        String foodType = r.getFoodType();
        //早餐
        if (meal.contains(breakfast)){
            //主食
            if (foodType.contains(zhushi)){
                breakfastzsList.add(r);
            }
            //辅食
            if (foodType.contains(fushi)){
                breakfastfsList.add(r);
            }
        }
        //中餐
        if (meal.contains(lunch)){
            //主食
            if (foodType.contains(zhushi)){
                lunchzsList.add(r);
            }
            //主菜
            if (foodType.contains(zhucai)){
                lunchzcList.add(r);
            }
            //辅菜
            if (foodType.contains(fucai)){
                lunchfcList.add(r);
            }
        }
        //晚餐
        if (meal.contains(supper)){
            //主食
            if (foodType.contains(zhushi)) {
                supperzsList.add(r);
            }
            //主菜
            if (foodType.contains(zhucai)) {
                supperzcList.add(r);
            }
            //辅菜
            if (foodType.contains(fucai)) {
                supperfcList.add(r);
            }
        }
        //加餐 甜品或饮品
        if (meal.contains(snack)){
            snackList.add(r);
        }

    }

    /**
     * 生成某一天全部菜谱
     * @param day 第几天
     * @return
     */
    public Map<String, Collection<RecipesTemplate>> getDayTemplate(int day){
        Map<String, Collection<RecipesTemplate>> rtn = new HashMap<>(32);
        int week = getWeek(day);
        String code = null;
        //整理餐次与餐品类型的整合
        List<int[]> meals = getMealsSetting();

        for(int idx=0, len=meals.size(); idx<len; idx++){
            int[] foods = meals.get(idx);
            for (int food : foods){
                int meal = idx+1;
                code = getCode(week, day, meal, food);
                Collection<Recipes> recipesColl = mealfood2Recipes.get(getMealFoodCode(meal, food));
                rtn.put(code, getRecipesTemplateList(recipesColl, code, week, day,
                        meal, food));
            }
        }
        return rtn;
    }

    /**
     * 整理餐次与餐品类型的整合
     * @return 餐次的安排设置
     */
    public List<int[]> getMealsSetting(){
        ArrayList<int[]> meals = new ArrayList<>(4);
        int[] breakfastArr = {FOOD_ZHUSHI, FOOD_FUSHI};
        int[] lunchArr = {FOOD_ZHUSHI, FOOD_ZHUCAI, FOOD_FUCAI};
        int[] supPerArr = {FOOD_ZHUSHI, FOOD_ZHUCAI, FOOD_FUCAI};
        int[] snackArr = {FOOD_TIANPIN};
        meals.add(MEAL_BREAKFAST-1, breakfastArr);
        meals.add(MEAL_LUNCH-1, lunchArr);
        meals.add(MEAL_SUPPER-1, supPerArr);
        meals.add(MEAL_SNACK-1, snackArr);
        return meals;
    }

    private String getMealFoodCode(int meal, int foodType){
        return String.format("%d%d", meal, foodType);
    }

    /**
     * 设置菜谱优先级
     * @param r 菜谱
     * @return
     */
    private Recipes changeWeekDay(Recipes r){
        //判断week和day 是否为文字，非文字全的菜谱，在type中记录weekday。
        //筛选菜谱时，type有值的优先，day优先级为2，week优先级为1，全优先级为0。
        String all = "全";
        String weekStr = r.getWeek();
        String dayStr = r.getDay();
        String level = low;
        if (!all.equals(dayStr)){
            //优先级最搞
            level = high;
        }else if(!all.equals(weekStr)){
            level = normal+"."+weekStr.replace("、","");
        }
        r.setType(level);
        return r;
    }

    private String changeWeek(String week){
        if ("全".equals(week)){
            week = "1,2,3,4";
        }else if (week.contains("、")){
            week = week.replace("、", ",");
        }
        return week;
    }

    private String changeDay(String day){
        if ("全".equals(day)){
            day = Month_First+"-"+Month_Last;
        }else if (day.contains("~")){
            day = day.replace("~","-");
        }
        return day;
    }

    private boolean compare(int day, String scope){
        boolean rtn = false;
        if (scope.contains("-")){
            String[] num = scope.split("-");
            rtn = (day >= Integer.parseInt(num[0]) && day <= Integer.parseInt(num[1]));
        }else{
            rtn = (day == Integer.parseInt(scope));
        }
        return rtn;
    }

    private String changeFeedType(String feedType){
        if ("全".equals(feedType)){
            feedType = "母乳,奶粉";
        }
        return feedType;
    }

    private String changeModeType(String modeType){
        if ("全".equals(modeType)){
            modeType = "顺产,剖腹产";
        }
        return modeType;
    }

    /**
     * 计算属于月子中的哪一周
     * @param day 大于0小于31
     * @return
     */
    public int getWeek(int day){
        int rtn = 0;
        if (day < 8){
            rtn = 1;
        }else if (day > 7 && day < 15){
            rtn = 2;
        }else if (day > 14 && day < 22){
            rtn = 3;
        }else{
            rtn = 4;
        }
        return rtn;
    }

    /**
     * 根据周、日、餐次、餐品类型，生成菜谱类型编码
     * @param week 周
     * @param day 日
     * @param meal 餐次
     * @param foodtype 餐品类型
     * @return
     */
    public String getCode(int week, int day, int meal, int foodtype){
        String format = "%d%02d%d%d";
        return String.format(format, week, day, meal, foodtype);
    }

    private Collection<RecipesTemplate> getRecipesTemplateList(Collection<Recipes> recipes, String code, int week,
                                           int day, int meal, int foodtype){
        List<RecipesTemplate> rtn = new ArrayList<>(16);
        for (Recipes r : recipes){
            String weekStr = ""+week, dayStr = ""+day;
            if (compare(day, r.getDay())){
                RecipesTemplate rt = new RecipesTemplate();
                rt.setRecipes(r);
                rt.analysisRecipes();
                rt.setCode(code);
                rt.setFoodType(Num2MealFood.get(foodtype));
                if (yinpin.equals(r.getFoodType())){
                    rt.setCode(getCode(week, day, meal, FOOD_YINPIN));
                    rt.setFoodType(yinpin);
                }
                rt.setMeal(Num2MealFood.get(meal));
                rt.setWeek(weekStr);
                rt.setDay(dayStr);
                rtn.add(rt);
            }
        }
        return rtn;
    }

}
